﻿using Mapster;
using System.Collections.Generic;
using System.Linq;
using YWM.Dto;
using YWM.Entities;
using YWM.Entities.Extensions;

namespace YWM.Application
{
    public abstract class CrudService<TEntity, TEntityDto>
        : CrudService<TEntity, TEntityDto, long>
        where TEntity : class, IEntityOfTKey<long>
        where TEntityDto : IEntityDto<long>
    {
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey>
        : CrudService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto>
        where TEntity : class, IEntityOfTKey<TKey>
        where TEntityDto : IEntityDto<TKey>
    {
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey, TGetAllInput>
        : CrudService<TEntity, TEntityDto, TKey, TGetAllInput, TEntityDto, TEntityDto>
        where TEntity : class, IEntityOfTKey<TKey>
        where TEntityDto : IEntityDto<TKey>
    {
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput>
        : CrudService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TCreateInput>
        where TEntity : class, IEntityOfTKey<TKey>
        where TEntityDto : IEntityDto<TKey>
        where TCreateInput : IEntityDto<TKey>
    {
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput>
        : CrudService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput, EntityDto<TKey>>
        where TEntity : class, IEntityOfTKey<TKey>
        where TEntityDto : IEntityDto<TKey>
        where TUpdateInput : IEntityDto<TKey>
    {
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput, TGetInput>
    : CrudService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput, TGetInput, EntityDto<TKey>>
        where TEntity : class, IEntityOfTKey<TKey>
        where TEntityDto : IEntityDto<TKey>
        where TUpdateInput : IEntityDto<TKey>
        where TGetInput : IEntityDto<TKey>
    {
    }

    public abstract class CrudService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput, TGetInput, TDeleteInput>
       : CrudServiceBase<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput>,
        ICrudService<TEntityDto, TKey, TGetAllInput, TCreateInput, TUpdateInput, TGetInput, TDeleteInput>
           where TEntity : class, IEntityOfTKey<TKey>
           where TEntityDto : IEntityDto<TKey>
           where TUpdateInput : IEntityDto<TKey>
           where TGetInput : IEntityDto<TKey>
           where TDeleteInput : IEntityDto<TKey>
    {
        public virtual TEntityDto Get(TGetInput input)
        {
            TEntity entity = GetEntityById(input.Id);
            return MapToEntityDto(entity);
        }

        public virtual PagedResultDto<TEntityDto> GetAll(TGetAllInput input)
        {
            IQueryable<TEntity> query = CreateFilteredQuery(input);

            int totalCount = query.Count();

            query = ApplySorting(query, input);
            query = ApplyPaging(query, input);

            List<TEntity> entities = query.ToList();

            return new PagedResultDto<TEntityDto>(
                totalCount,
                entities.Select(MapToEntityDto).ToList()
            );
        }

        public virtual TEntityDto Create(TCreateInput input)
        {
            TEntity entity = MapToEntity(input);

            Repository.Insert(entity);

            DbContextPool.SavePoolNow();

            return MapToEntityDto(entity);
        }

        public virtual TEntityDto Update(TUpdateInput input)
        {
            return Update(input);
        }

        public virtual void Delete(TDeleteInput input)
        {
            if (input.IsSoftDelete())
            {
                Update(input);
            }
            else
            {
                Repository.Delete(input.Id);

                DbContextPool.SavePoolNow();
            }            
        }

        private TEntityDto Update<TInput>(TInput input) where TInput : IEntityDto<TKey>
        {
            TEntity entity = GetEntityById(input.Id);

            entity = input.Adapt(entity);

            DbContextPool.SavePoolNow();

            return MapToEntityDto(entity);
        }

        protected virtual TEntity GetEntityById(TKey id)
        {
            return Repository.Get(id);
        }
    }
}
